package easy.trace.core.support;

import easy.trace.core.context.RpcIdContext;
import easy.trace.core.context.SpanContext;
import easy.trace.core.common.Constants;
import easy.trace.common.Annotation;
import easy.trace.common.BinaryAnnotation;
import easy.trace.common.Span;
import easy.trace.core.util.StringUtils;

import java.util.Optional;

/**
 * @author easy
 */
public class SpanBuilder {
    private SpanContext spanContext;
    private RpcIdContext rpcIdContext;

    public SpanBuilder() {
        this.spanContext = new SpanContext();
        this.rpcIdContext = new RpcIdContext();
    }

    private static class SpanContextBuilder {
        static SpanBuilder instance = new SpanBuilder();
    }

    public static SpanBuilder getSpanContextBuilder() {
        return SpanContextBuilder.instance;
    }

    public void setCurrentSpan(Span span) {
        spanContext.setCurrentSpan(span);
    }

    public Span getCurrentSpan() {
        return spanContext.getCurrentSpan();
    }


    public void setParentSpan(Span span) {
        spanContext.setParentSpan(span);
    }

    public Span getParentSpan() {
        return spanContext.getParentSpan();
    }

    public void removeParentSpan() {
        spanContext.removeParentSpan();
    }

    public void removeCurrentSpan() {
        spanContext.removeParentSpan();
    }


    public void setLocalRpcId(String rpcId) {
        rpcIdContext.setLocalRpcId(rpcId);
    }

    public String getLocalRpcId() {
        return rpcIdContext.getLocalRpcId();
    }

    public void removeLocalRpcId() {
        rpcIdContext.removeLocalRpcId();
    }

    /**
     * 构件cs annotation
     *
     * @return
     */
    public Span createCsSpan(String serviceId, String methodName, String eventType, Long traceId, Long id) {
        return createCsSpan(serviceId, methodName, eventType, traceId, id, "127.0.0.1", 0);
    }

    /**
     * 构件cs annotation
     *
     * @return
     */
    public Span createCsSpan(String serviceId, String methodName, String eventType, Long traceId, Long parentId, String address, int port) {
        Span newSpan = spanContext.createNewSpan(serviceId, methodName, eventType, traceId, parentId);
        Annotation newAnnotation = spanContext.createNewAnnotation(Annotation.CLIENT_SEND, address, port);
        addAnnotationWithCheck(newSpan,newAnnotation);
        return newSpan;
    }

    /**
     * 构件cs annotation
     *
     * @return
     */
    public Span createCsSpan(String serviceId, String methodName, String eventType, String address, int port) {
        Span newSpan = spanContext.createNewSpan(serviceId, methodName, eventType);
        Annotation newAnnotation = spanContext.createNewAnnotation(Annotation.CLIENT_SEND, address, port);
        addAnnotationWithCheck(newSpan,newAnnotation);
        return newSpan;
    }

    /**
     * 构件cs annotation
     *
     * @return
     */
    public Span createCsSpan(String serviceId, String methodName, String eventType) {
        return createCsSpan(serviceId, methodName, eventType, "127.0.0.1", 0);
    }

    /**
     * 构件sr annotation
     *
     * @return
     */
    public Span createSrSpan(String serviceId, String methodName, String eventType, Long traceId, Long parentId, Long selfId) {
        Span newSpan = spanContext.createNewSpan(serviceId, methodName, eventType, traceId, parentId, selfId);
        Annotation newAnnotation = spanContext.createNewAnnotation(Annotation.SERVER_RECEIVE);
        addAnnotationWithCheck(newSpan,newAnnotation);
        return newSpan;
    }

    /**
     * 构件sr annotation
     *
     * @return
     */
    public Span createSrSpan(String serviceId, String methodName, String eventType, Long traceId, Long parentId, Long selfId, String address, int port) {
        Span newSpan = spanContext.createNewSpan(serviceId, methodName, eventType, traceId, parentId, selfId);
        Annotation newAnnotation = spanContext.createNewAnnotation(Annotation.SERVER_RECEIVE, address, port);
        addAnnotationWithCheck(newSpan,newAnnotation);
        return newSpan;
    }

    /**
     * 构件sr annotation
     *
     * @param parentSpan
     * @param address
     * @param port
     */
    public void addSrAnnotation(Span parentSpan, String address, int port) {
        Annotation newAnnotation = spanContext.createNewAnnotation(Annotation.SERVER_RECEIVE, address, port);
        addAnnotationWithCheck(parentSpan,newAnnotation);
    }

    /**
     * 构件ss annotation
     *
     * @return
     */
    public void addSsAnnotation(Span parentSpan) {
        addSsAnnotation(parentSpan, Constants.DEFAULT_IP, Constants.DEFAULT_PORT);
    }

    /**
     * @param parentSpan
     * @param address，
     * @param port
     */
    public void addSsAnnotation(Span parentSpan, String address, int port) {
        Annotation newAnnotation = spanContext.createNewAnnotation(Annotation.SERVER_SEND, address, port);
        addAnnotationWithCheck(parentSpan,newAnnotation);
    }

    /**
     * 构件cr annotation
     *
     * @param parentSpan
     * @return
     */
    public void addCrAnnotation(Span parentSpan) {
        addCrAnnotation(parentSpan, Constants.DEFAULT_IP, Constants.DEFAULT_PORT);
    }

    /**
     * @param parentSpan
     * @param address
     * @param port
     */
    public void addCrAnnotation(Span parentSpan, String address, int port) {
        Annotation newAnnotation = spanContext.createNewAnnotation(Annotation.CLIENT_RECEIVE, address, port);
        addAnnotationWithCheck(parentSpan,newAnnotation);
    }

    private void addAnnotationWithCheck(Span span, Annotation annotation) {
        if (span != null && span.getAnnotations() != null && span.getAnnotations().size() <= 4) {
            span.addAnnotation(annotation);
        }
    }

    /**
     * 超时异常 annotation
     *
     * @param parentSpan
     * @param message
     */
    public void addExceptionAnnotation(Span parentSpan, String message) {
        BinaryAnnotation newTimeOutAnnotation = spanContext.createNewTimeOutAnnotation(message);
        parentSpan.addBinaryAnnotation(newTimeOutAnnotation);
    }

    /**
     * 超时异常 annotation
     *
     * @param parentSpan
     * @param message
     */
    public void addExceptionAnnotation(Span parentSpan, String message, String messageType) {
        BinaryAnnotation newTimeOutAnnotation = spanContext.createNewTimeOutAnnotation(message, messageType);
        parentSpan.addBinaryAnnotation(newTimeOutAnnotation);
    }


    public void upsertRpcId(Span parentSpan, String serviceId, String methodName) {
        if (StringUtils.nonEmpty(rpcIdContext.getLocalRpcId())) {
            appendRpcId(parentSpan, serviceId, methodName);
        } else {
            createRpcId(parentSpan, serviceId, methodName);
        }
    }

    /**
     * 创建rpcId
     *
     * @param serviceId
     * @param methodName
     */
    public void createRpcId(Span parentSpan, String serviceId, String methodName) {
        Optional.ofNullable(rpcIdContext.genRpcId(serviceId, methodName))
                .filter(StringUtils::isEmpty)
                .ifPresent((oldRpcId) -> {
                    rpcIdContext.setLocalRpcId(oldRpcId);
                    parentSpan.setRpcId(oldRpcId);
                });
    }


    /**
     * 追加rpcId
     *
     * @param parentSpan
     * @param serviceId
     * @param methodName
     */
    public void appendRpcId(Span parentSpan, String serviceId, String methodName) {
        Optional.ofNullable(rpcIdContext.getLocalRpcId())
                .filter(StringUtils::isEmpty)
                .ifPresent((oldRpcId) -> {
                    String rpcId = rpcIdContext.genRpcId(oldRpcId, serviceId, methodName);
                    parentSpan.setRpcId(rpcId);
                });
    }

}
